package controllers;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import callbacks.GMailCallbacks;

import com.ning.http.client.Body;
import com.sun.mail.gimap.GmailSSLStore;

import models.EmailData;
import models.GmailData;
import models.YouUser;
import play.libs.F;
import play.libs.Json;
import play.libs.F.Function;
import play.libs.F.Option;
import play.libs.F.Promise;
import play.libs.WS.Response;
import play.mvc.Result;
import services.GoogleService;
import util.OAuthUtil;
import views.html.index;

public class Application extends GMailCallbacks {

	public static Result search(long youId, String query, int max) {
		YouUser user = YouUser.find.byId(youId);
		GmailData data = user.getGmailData();
		
		if (data != null) {
			Promise<GmailSSLStore> gmailStorePromise = GoogleService
					.getGmailStore(data.email, data.accessToken);
			return async(gmailStorePromise
					.flatMap(new ConnectToStore(data.email))
					.flatMap(new StoreToFolder("INBOX"))
					.flatMap(new FolderToSearch(query))
					.flatMap(new MessagesToFetch())
					.flatMap(new MessagesToRequest(max)));
		} else {
			return internalServerError("Specified user " + youId
					+ " doesn't have gmail info linked to his account");
		}
	}

	public static Result createUser() {
		YouUser user = YouUser.create();
		return ok(Json.toJson(user));
	}
	
	public static Result link(long youId, Option<String> callback) {
		String authRedirect = OAuthUtil.getGoogleAuthUrl(youId, callback);
		return redirect(authRedirect);
	}

	public static Result linkCallback(Option<String> code, Option<String> state,
			Option<String> error) {
		if (!state.isDefined()) {
			return badRequest("YOU-ID not returned - check 'state' parameter");			
		}
		if (!code.isDefined()) {
			if (error.isDefined()) {
				return badRequest(error.get());
			} else { // no error, redirect to google auth page
				return badRequest("No code returned - cannot proceed");
			}
		}

		String[] stateInfo = state.get().split("\\$\\$"); 
		Long youId = Long.parseLong(stateInfo[0]);
		
		final String callbackUrl;
		if (stateInfo.length > 1) {
			callbackUrl = stateInfo[1];
		} else {
			callbackUrl = "";
		}
		
		String authcode = code.get();
		Promise<YouUser> userPromise = GoogleService.storeOrUpdateUser(youId,
				authcode);
		return async(userPromise.map(new Function<YouUser, Result>() {
			@Override
			public Result apply(YouUser a) throws Throwable {
				if (callbackUrl.equals("")) {
					return ok(a.toString());
				} else {
					return redirect(callbackUrl + "?id=" + a.youId + "&success=true");
				}
			}
		}));
	}
	
	public static Result unlink(long youId) {
		session().clear();
		YouUser user = YouUser.find.byId(youId);
		Set<EmailData> toUnlink = user.clearLinks();
		List<Promise<? extends Response>> unlinkPromises = new ArrayList<Promise<? extends Response>>();
		
		for (EmailData linkData : toUnlink) {
			Promise<Response> promise = linkData.unlink();
			unlinkPromises.add(promise);
		}
		Promise<List<Response>> combinedPromise = Promise.sequence(unlinkPromises);
		
		return async(combinedPromise.map(new Function<List<Response>, Result>() {
			@Override
			public Result apply(List<Response> arg0) throws Throwable {
				boolean ok = true;
				List<String> errors = new ArrayList<String>();
				
				for (Response r : arg0) {
					if (r.getStatus() != 200) {
						ok = false;
						errors.add(r.getBody());
					}
				}
				
				if (ok) {
					return ok("accounts successfuly unlinked");
				} else {
					return badRequest("error revoking tokens: "
							+ errors.toString());
				}
			}
		}));
	}

	public static Result index() {
		return ok(index.render("Your new application is ready."));
	}

}
